/*
 *  Vide - v0.5.1
 *  Easy as hell jQuery plugin for video backgrounds.
 *  http://vodkabears.github.io/vide/
 *
 *  Made by Ilya Makarov
 *  Under MIT License
 */
!(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        factory(require('jquery'));
    } else {
        factory(root.jQuery);
    }
})(this, function ($) {

    'use strict';

    /**
     * Name of the plugin
     * @private
     * @const
     * @type {String}
     */
    var PLUGIN_NAME = 'vide';

    /**
     * Default settings
     * @private
     * @const
     * @type {Object}
     */
    var DEFAULTS = {
        volume: 1,
        playbackRate: 1,
        muted: true,
        loop: true,
        autoplay: true,
        position: '50% 50%',
        posterType: 'detect',
        resizing: true,
        bgColor: 'transparent',
        className: ''
    };

    /**
     * Not implemented error message
     * @private
     * @const
     * @type {String}
     */
    var NOT_IMPLEMENTED_MSG = 'Not implemented';

    /**
     * Parse a string with options
     * @private
     * @param {String} str
     * @returns {Object|String}
     */
    function parseOptions(str) {
        var obj = {};
        var delimiterIndex;
        var option;
        var prop;
        var val;
        var arr;
        var len;
        var i;

        // Remove spaces around delimiters and split
        arr = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',').split(',');

        // Parse a string
        for (i = 0, len = arr.length; i < len; i++) {
            option = arr[i];

            // Ignore urls and a string without colon delimiters
            if (
                option.search(/^(http|https|ftp):\/\//) !== -1 ||
                option.search(':') === -1
            ) {
                break;
            }

            delimiterIndex = option.indexOf(':');
            prop = option.substring(0, delimiterIndex);
            val = option.substring(delimiterIndex + 1);

            // If val is an empty string, make it undefined
            if (!val) {
                val = undefined;
            }

            // Convert a string value if it is like a boolean
            if (typeof val === 'string') {
                val = val === 'true' || (val === 'false' ? false : val);
            }

            // Convert a string value if it is like a number
            if (typeof val === 'string') {
                val = !isNaN(val) ? +val : val;
            }

            obj[prop] = val;
        }

        // If nothing is parsed
        if (prop == null && val == null) {
            return str;
        }

        return obj;
    }

    /**
     * Parse a position option
     * @private
     * @param {String} str
     * @returns {Object}
     */
    function parsePosition(str) {
        str = '' + str;

        // Default value is a center
        var args = str.split(/\s+/);
        var x = '50%';
        var y = '50%';
        var len;
        var arg;
        var i;

        for (i = 0, len = args.length; i < len; i++) {
            arg = args[i];

            // Convert values
            if (arg === 'left') {
                x = '0%';
            } else if (arg === 'right') {
                x = '100%';
            } else if (arg === 'top') {
                y = '0%';
            } else if (arg === 'bottom') {
                y = '100%';
            } else if (arg === 'center') {
                if (i === 0) {
                    x = '50%';
                } else {
                    y = '50%';
                }
            } else {
                if (i === 0) {
                    x = arg;
                } else {
                    y = arg;
                }
            }
        }

        return {x: x, y: y};
    }

    /**
     * Search a poster
     * @private
     * @param {String} path
     * @param {Function} callback
     */
    function findPoster(path, callback) {
        var onLoad = function () {
            callback(this.src);
        };

    }

    /**
     * Vide constructor
     * @param {HTMLElement} element
     * @param {Object|String} path
     * @param {Object|String} options
     * @constructor
     */
    function Vide(element, path, options) {
        this.$element = $(element);

        // Parse path
        if (typeof path === 'string') {
            path = parseOptions(path);
        }

        // Parse options
        if (!options) {
            options = {};
        } else if (typeof options === 'string') {
            options = parseOptions(options);
        }

        // Remove an extension
        if (typeof path === 'string') {
            path = path.replace(/\.\w*$/, '');
        } else if (typeof path === 'object') {
            for (var i in path) {
                if (path.hasOwnProperty(i)) {
                    path[i] = path[i].replace(/\.\w*$/, '');
                }
            }
        }

        this.settings = $.extend({}, DEFAULTS, options);
        this.path = path;

        // https://github.com/VodkaBears/Vide/issues/110
        try {
            this.init();
        } catch (e) {
            if (e.message !== NOT_IMPLEMENTED_MSG) {
                throw e;
            }
        }
    }

    /**
     * Initialization
     * @public
     */
    Vide.prototype.init = function () {
        var vide = this;
        var path = vide.path;
        var poster = path;
        var sources = '';
        var $element = vide.$element;
        var settings = vide.settings;
        var position = parsePosition(settings.position);
        var posterType = settings.posterType;
        var $video;
        var $wrapper;

        // Set styles of a video wrapper
        $wrapper = vide.$wrapper = $('<div>')
            .addClass(settings.className)
            .css({
                position: 'absolute',
                'z-index': -1,
                top: 0,
                left: 0,
                bottom: 0,
                right: 0,
                overflow: 'hidden',
                '-webkit-background-size': 'cover',
                '-moz-background-size': 'cover',
                '-o-background-size': 'cover',
                'background-size': 'cover',
                'background-color': settings.bgColor,
                'background-repeat': 'no-repeat',
                'background-position': position.x + ' ' + position.y
            });

        // Get a poster path
        if (typeof path === 'object') {
            if (path.poster) {
                poster = path.poster;
            } else {
                if (path.mp4) {
                    poster = path.mp4;
                } else if (path.webm) {
                    poster = path.webm;
                } else if (path.ogv) {
                    poster = path.ogv;
                }
            }
        }

        // Set a video poster
        if (posterType === 'detect') {
            findPoster(poster, function (url) {
                $wrapper.css('background-image', 'url(' + url + ')');
            });
        } else if (posterType !== 'none') {
            $wrapper.css('background-image', 'url(' + poster + '.' + posterType + ')');
        }

        // If a parent element has a static position, make it relative
        if ($element.css('position') === 'static') {
            $element.css('position', 'static');
        }

        $element.prepend($wrapper);

        if (typeof path === 'object') {
            if (path.mp4) {
                sources += '<source src="' + path.mp4 + '.mp4" type="video/mp4">';
            }

            if (path.webm) {
                sources += '<source src="' + path.webm + '.webm" type="video/webm">';
            }

            if (path.ogv) {
                sources += '<source src="' + path.ogv + '.ogv" type="video/ogg">';
            }

            $video = vide.$video = $('<video>' + sources + '</video>');
        } else {
            $video = vide.$video = $('<video>' +
                '<source src="' + path + '.mp4" type="video/mp4">' +
                '<source src="' + path + '.webm" type="video/webm">' +
                '<source src="' + path + '.ogv" type="video/ogg">' +
                '</video>');
        }

        // https://github.com/VodkaBears/Vide/issues/110
        try {
            $video

                // Set video properties
                .prop({
                    autoplay: settings.autoplay,
                    loop: settings.loop,
                    volume: settings.volume,
                    muted: settings.muted,
                    defaultMuted: settings.muted,
                    playbackRate: settings.playbackRate,
                    defaultPlaybackRate: settings.playbackRate
                });
        } catch (e) {
            throw new Error(NOT_IMPLEMENTED_MSG);
        }

        // Video alignment
        $video.css({
            margin: 'auto',
            position: 'absolute',
            'z-index': -1,
            top: position.y,
            left: position.x,
            '-webkit-transform': 'translate(-' + position.x + ', -' + position.y + ')',
            '-ms-transform': 'translate(-' + position.x + ', -' + position.y + ')',
            '-moz-transform': 'translate(-' + position.x + ', -' + position.y + ')',
            transform: 'translate(-' + position.x + ', -' + position.y + ')',

            // Disable visibility, while loading
            visibility: 'hidden',
            opacity: 0
        })

            // Resize a video, when it's loaded
            .one('canplaythrough.' + PLUGIN_NAME, function () {
                vide.resize();
            })

            // Make it visible, when it's already playing
            .one('playing.' + PLUGIN_NAME, function () {
                $video.css({
                    visibility: 'visible',
                    opacity: 1
                });
                $wrapper.css('background-image', 'none');
            });

        // Resize event is available only for 'window'
        // Use another code solutions to detect DOM elements resizing
        $element.on('resize.' + PLUGIN_NAME, function () {
            if (settings.resizing) {
                vide.resize();
            }
        });

        // Append a video
        $wrapper.append($video);
    };

    /**
     * Get a video element
     * @public
     * @returns {HTMLVideoElement}
     */
    Vide.prototype.getVideoObject = function () {
        return this.$video[0];
    };

    /**
     * Resize a video background
     * @public
     */
    Vide.prototype.resize = function () {
        if (!this.$video) {
            return;
        }

        var $wrapper = this.$wrapper;
        var $video = this.$video;
        var video = $video[0];

        // Get a native video size
        var videoHeight = video.videoHeight;
        var videoWidth = video.videoWidth;

        // Get a wrapper size
        var wrapperHeight = $wrapper.height();
        var wrapperWidth = $wrapper.width();

        if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) {
            $video.css({

                // +2 pixels to prevent an empty space after transformation
                width: wrapperWidth + 2,
                height: 'auto'
            });
        } else {
            $video.css({
                width: 'auto',

                // +2 pixels to prevent an empty space after transformation
                height: wrapperHeight + 2
            });
        }
    };

    /**
     * Destroy a video background
     * @public
     */
    Vide.prototype.destroy = function () {
        delete $[PLUGIN_NAME].lookup[this.index];
        this.$video && this.$video.off(PLUGIN_NAME);
        this.$element.off(PLUGIN_NAME).removeData(PLUGIN_NAME);
        this.$wrapper.remove();
    };

    /**
     * Special plugin object for instances.
     * @public
     * @type {Object}
     */
    $[PLUGIN_NAME] = {
        lookup: []
    };

    /**
     * Plugin constructor
     * @param {Object|String} path
     * @param {Object|String} options
     * @returns {JQuery}
     * @constructor
     */
    $.fn[PLUGIN_NAME] = function (path, options) {
        var instance;

        this.each(function () {
            instance = $.data(this, PLUGIN_NAME);

            // Destroy the plugin instance if exists
            instance && instance.destroy();

            // Create the plugin instance
            instance = new Vide(this, path, options);
            instance.index = $[PLUGIN_NAME].lookup.push(instance) - 1;
            $.data(this, PLUGIN_NAME, instance);
        });

        return this;
    };

    $(document).ready(function () {
        var $window = $(window);

        // Window resize event listener
        $window.on('resize.' + PLUGIN_NAME, function () {
            for (var len = $[PLUGIN_NAME].lookup.length, i = 0, instance; i < len; i++) {
                instance = $[PLUGIN_NAME].lookup[i];

                if (instance && instance.settings.resizing) {
                    instance.resize();
                }
            }
        });

        // https://github.com/VodkaBears/Vide/issues/68
        $window.on('unload.' + PLUGIN_NAME, function () {
            return false;
        });

        // Auto initialization
        // Add 'data-vide-bg' attribute with a path to the video without extension
        // Also you can pass options throw the 'data-vide-options' attribute
        // 'data-vide-options' must be like 'muted: false, volume: 0.5'
        $(document).find('[data-' + PLUGIN_NAME + '-bg]').each(function (i, element) {
            var $element = $(element);
            var options = $element.data(PLUGIN_NAME + '-options');
            var path = $element.data(PLUGIN_NAME + '-bg');

            $element[PLUGIN_NAME](path, options);
        });
    });

});
